/** @file q3_bsp.cpp
    @brief Quake 3 BSP */

#include "q3/q3_bsp.h"

using namespace G3;

string G3::Q3BSP_LUMPNAMES [17] = {
	"Entities",
	"Textures",
	"Planes",
	"Nodes",
	"Leaves",
	"LeafFaces",
	"LeafBrushes",
	"Models",
	"Brushes",
	"BrushSides",
	"Vertices",
	"MeshIndices",
	"Effects",
	"Faces",
	"Lightmaps",
	"LightVols",
	"VisData"
};

//==============================================================================
// Constructors
//==============================================================================
//{
G3::Q3BSP_LUMP::Q3BSP_LUMP () {
	FileOffset = 0;
	FileLength = 0;
}

G3::Q3BSP_HEADER::Q3BSP_HEADER () {
	Signature [0] = 'I';
	Signature [1] = 'B';
	Signature [2] = 'S';
	Signature [3] = 'P';

	Version = 0;
}

G3::Q3BSP_LUMP_ENTITIES::Q3BSP_LUMP_ENTITIES () {
	Size = 0;
	pEntities = NULL;
}

G3::Q3BSP_LUMP_TEXTURE::Q3BSP_LUMP_TEXTURE () {
	memset (Name, 0, 64 * sizeof (char));
	SurfaceFlags = 0;
	ContentFlags = 0;
}

G3::Q3BSP_LUMP_PLANE::Q3BSP_LUMP_PLANE () {
	Normal [0] = 0.0f;
	Normal [1] = 0.0f;
	Normal [2] = 0.0f;
	Dist = 0.0f;
}

G3::Q3BSP_LUMP_NODE::Q3BSP_LUMP_NODE () {
	Plane = 0;
	Children [0] = 0;
	Children [1] = 0;
	memset (BBox, 0, 6 * sizeof (float));
}

G3::Q3BSP_LUMP_LEAF::Q3BSP_LUMP_LEAF () {
	Cluster = 0;
	Area = 0;
	memset (BBox, 0, 6 * sizeof (float));
	FirstFace = 0;
	NumFaces = 0;
	FirstBrush = 0;
	NumBrushes = 0;
}

G3::Q3BSP_LUMP_LEAF_FACE::Q3BSP_LUMP_LEAF_FACE () {
	FaceIndex = 0;
}

G3::Q3BSP_LUMP_LEAF_BRUSH::Q3BSP_LUMP_LEAF_BRUSH () {
	BrushIndex = 0;
}

G3::Q3BSP_LUMP_MODEL::Q3BSP_LUMP_MODEL () {
	memset (BBox, 0, 6 * sizeof (float));
	FirstFace = 0;
	NumFaces = 0;
	FirstBrush = 0;
	NumBrushes = 0;
}

G3::Q3BSP_LUMP_BRUSH::Q3BSP_LUMP_BRUSH () {
	FirstSide = 0;
	NumSides = 0;
	TexIndex = 0;
}

G3::Q3BSP_LUMP_BRUSH_SIDE::Q3BSP_LUMP_BRUSH_SIDE () {
	PlaneNum = 0;
	TexIndex = 0;
}

G3::Q3BSP_LUMP_VERTEX::Q3BSP_LUMP_VERTEX () {
	memset (Point, 0, 3 * sizeof (float));
	memset (TexCoord, 0, 2 * sizeof (float));
	memset (LightTexCoord, 0, 2 * sizeof (float));
	memset (Normal, 0, 3 * sizeof (float));
	Color = 0;
}

G3::Q3BSP_LUMP_MESH_VERTEX::Q3BSP_LUMP_MESH_VERTEX () {
	Offset = 0;
}

G3::Q3BSP_LUMP_EFFECT::Q3BSP_LUMP_EFFECT () {
	memset (Name, 0, 64 * sizeof (char));
	Brush = 0;
	Unknown = 0;
}

G3::Q3BSP_LUMP_FACE::Q3BSP_LUMP_FACE () {
	int i = 0;

	ShaderIndex = 0;
	EffectIndex = 0;
	FaceType = 0;
	FirstVert = 0;
	NumVerts = 0;
	FirstMeshVerts = 0;
	NumMeshVerts = 0;
	LightmapIndex = 0;

	for (i=0; i<2; i++) {
		LightmapStart [i] = 0;
		LightmapSize [i] = 0;
		PatchSize [i] = 0;
	}
	for (i=0; i<3; i++) {
		LightmapOrigin [i] = 0.0f;
		Normal [i] = 0.0f;
	}
	for (i=0; i<3; i++) {
		LightmapVects [0][i] = 0.0f;
		LightmapVects [1][i] = 0.0f;
	}
}

G3::Q3BSP_LUMP_LIGHTMAP::Q3BSP_LUMP_LIGHTMAP () {
	memset (Map, 0, 49152 * sizeof (byte));
}

G3::Q3BSP_LUMP_LIGHTVOL::Q3BSP_LUMP_LIGHTVOL () {
	for (int i=0; i<3; i++) {
		Ambient [i] = 0;
		Directional [i] = 0;
	}
	Direction [0] = 0;
	Direction [1] = 0;
}

G3::Q3BSP_LUMP_VISIBILITY::Q3BSP_LUMP_VISIBILITY () {
	NumClusters = 0;
	BytesPerCluster = 0;
	Buffer = NULL;
}

G3::Q3BSP_NODE::Q3BSP_NODE () {
	Log = &G3::MainLog;
	LastLeaf = 0;
	UsePVS = true;
	UseFrustumCulling = true;
	RenderColorArrays = true;
	RenderTexCoordArrays = true;

	LastIntersectionBrush = -1;
	LastIntersectionStartPlane = -1;
	LastIntersectionEndPlane = -1;

	LastLeaf = 0;
	LastIntersectionBrush = 0;
	LastIntersectionStartPlane = 0;
    LastIntersectionEndPlane = 0;

    RenderBBoxes = false;
}
//}

//==============================================================================
// Destructors
//==============================================================================
G3::Q3BSP_NODE::~Q3BSP_NODE () {
	Header.Clear ();
	Entities.Clear ();
	VisData.Clear ();

	Textures.clear ();
	Planes.clear ();
	Nodes.clear ();
	Leaves.clear ();
	LeafFaces.clear ();
	LeafBrushes.clear ();
	Models.clear ();
	Brushes.clear ();
	BrushSides.clear ();
	Vertices.clear ();
	MeshVertices.clear ();
	Effects.clear ();
	Faces.clear ();
	Lightmaps.clear ();
	LightVols.clear ();

	LeavesInFrustum.clear ();
	LeavesSpanFrustum.clear ();
}

//==============================================================================
// = operators
//==============================================================================
//{
Q3BSP_LUMP& G3::Q3BSP_LUMP::operator=(const Q3BSP_LUMP &l) {
	FileOffset = l.FileOffset;
	FileLength = l.FileLength;

	return *this;
}

Q3BSP_HEADER& G3::Q3BSP_HEADER::operator=(const Q3BSP_HEADER &h) {
	memcpy (Signature, h.Signature, 4 * sizeof (char));
	Version = h.Version;
	memcpy (Lumps, h.Lumps, 17 * Lumps [0].GetSize ());

	return *this;
}

Q3BSP_LUMP_ENTITIES& G3::Q3BSP_LUMP_ENTITIES::operator=(const Q3BSP_LUMP_ENTITIES &e) {
	Size = e.Size;

	if (e.pEntities) {
        delete [] pEntities;

		pEntities = new char [Size];
		memcpy (pEntities, e.pEntities, Size * sizeof (char));
	}

	return *this;
}

Q3BSP_LUMP_TEXTURE& G3::Q3BSP_LUMP_TEXTURE::operator=(const Q3BSP_LUMP_TEXTURE &t) {
	memcpy (Name, t.Name, 64 * sizeof (char));
	SurfaceFlags = t.SurfaceFlags;
	ContentFlags = t.ContentFlags;
	Handle = t.Handle;

	return *this;
}

Q3BSP_LUMP_PLANE& G3::Q3BSP_LUMP_PLANE::operator=(const Q3BSP_LUMP_PLANE &p) {
	memcpy (Normal, p.Normal, 3 * sizeof (float));
	Dist = p.Dist;

	return *this;
}

Q3BSP_LUMP_NODE& G3::Q3BSP_LUMP_NODE::operator=(const Q3BSP_LUMP_NODE &n) {
	Plane = n.Plane;
	memcpy (Children, n.Children, 2 * sizeof (int));
	memcpy (BBox, n.BBox, 6 * sizeof (float));

	return *this;
}

Q3BSP_LUMP_LEAF& G3::Q3BSP_LUMP_LEAF::operator=(const Q3BSP_LUMP_LEAF &l) {
	Cluster = l.Cluster;
	Area = l.Area;
	memcpy (BBox, l.BBox, 6 * sizeof (float));
	FirstFace = l.FirstFace;
	NumFaces = l.NumFaces;
	FirstBrush = l.FirstBrush;
	NumBrushes = l.NumBrushes;

	return *this;
}

Q3BSP_LUMP_LEAF_FACE& G3::Q3BSP_LUMP_LEAF_FACE::operator= (const Q3BSP_LUMP_LEAF_FACE &lf) {
	FaceIndex = lf.FaceIndex;

	return *this;
}

Q3BSP_LUMP_LEAF_BRUSH& G3::Q3BSP_LUMP_LEAF_BRUSH::operator=(const Q3BSP_LUMP_LEAF_BRUSH &lb) {
	BrushIndex = lb.BrushIndex;

	return *this;
}

Q3BSP_LUMP_MODEL& G3::Q3BSP_LUMP_MODEL::operator=(const Q3BSP_LUMP_MODEL &m) {
	memcpy (BBox, m.BBox, 6 * sizeof (float));
	FirstFace = m.FirstFace;
	NumFaces = m.NumFaces;
	FirstBrush = m.FirstBrush;
	NumBrushes = m.NumBrushes;

	return *this;
}

Q3BSP_LUMP_BRUSH& G3::Q3BSP_LUMP_BRUSH::operator=(const Q3BSP_LUMP_BRUSH &b) {
	FirstSide = b.FirstSide;
	NumSides = b.NumSides;
	TexIndex = b.TexIndex;

	return *this;
}

Q3BSP_LUMP_BRUSH_SIDE& G3::Q3BSP_LUMP_BRUSH_SIDE::operator=(const Q3BSP_LUMP_BRUSH_SIDE &bs) {
	PlaneNum = bs.PlaneNum;
	TexIndex = bs.TexIndex;

	return *this;
}

Q3BSP_LUMP_VERTEX& G3::Q3BSP_LUMP_VERTEX::operator=(const Q3BSP_LUMP_VERTEX &lv) {
	memcpy (Point, lv.Point, 3 * sizeof (float));
	memcpy (TexCoord, lv.TexCoord, 2 * sizeof (float));
	memcpy (LightTexCoord, lv.LightTexCoord, 2 * sizeof (float));
	memcpy (Normal, lv.Normal, 3 * sizeof (float));
	Color = lv.Color;

	return *this;
}

Q3BSP_LUMP_MESH_VERTEX& G3::Q3BSP_LUMP_MESH_VERTEX::operator=(const Q3BSP_LUMP_MESH_VERTEX &mv) {
	Offset = mv.Offset;

	return *this;
}

Q3BSP_LUMP_EFFECT& G3::Q3BSP_LUMP_EFFECT::operator=(const Q3BSP_LUMP_EFFECT &e) {
	memcpy (Name, e.Name, 64 * sizeof (char));
	Brush = e.Brush;
	Unknown = e.Unknown;

	return *this;
}

Q3BSP_LUMP_FACE& G3::Q3BSP_LUMP_FACE::operator=(const Q3BSP_LUMP_FACE &f) {
	ShaderIndex = f.ShaderIndex;
	EffectIndex = f.EffectIndex;
	FaceType = f.FaceType;
	FirstVert = f.FirstVert;
	NumVerts = f.NumVerts;
	FirstMeshVerts = f.FirstMeshVerts;
	NumMeshVerts = f.NumMeshVerts;
	LightmapIndex = f.LightmapIndex;
	memcpy (LightmapStart, f.LightmapStart, 2 * sizeof (int));
	memcpy (LightmapSize, f.LightmapSize, 2 * sizeof (int));
	memcpy (LightmapOrigin, f.LightmapOrigin, 3 * sizeof (float));
	memcpy (LightmapVects, f.LightmapVects, 6 * sizeof (float));
	memcpy (Normal, f.Normal, 3 * sizeof (float));
	memcpy (PatchSize, f.PatchSize, 2 * sizeof (int));

	return *this;
}

Q3BSP_LUMP_LIGHTMAP& G3::Q3BSP_LUMP_LIGHTMAP::operator=(const Q3BSP_LUMP_LIGHTMAP &l) {
	memcpy (Map, l.Map, 49152 * sizeof (byte));

	return *this;
}

Q3BSP_LUMP_LIGHTVOL& G3::Q3BSP_LUMP_LIGHTVOL::operator=(const Q3BSP_LUMP_LIGHTVOL &lv) {
	memcpy (Ambient, lv.Ambient, 3 * sizeof (byte));
	memcpy (Directional, lv.Directional, 3 * sizeof (byte));
	memcpy (Direction, lv.Direction, 2 * sizeof (byte));

	return *this;
}

Q3BSP_LUMP_VISIBILITY& G3::Q3BSP_LUMP_VISIBILITY::operator=(const Q3BSP_LUMP_VISIBILITY &v) {
	NumClusters = v.NumClusters;
	BytesPerCluster = v.BytesPerCluster;
	if (v.Buffer) {
        delete [] Buffer;

		Buffer = new byte [NumClusters * BytesPerCluster];
		memcpy (Buffer, v.Buffer, NumClusters * BytesPerCluster * sizeof (byte));
	}

	return *this;
}
//}

//==============================================================================
// Functions that get the size of the classes
//==============================================================================
//{
long G3::Q3BSP_LUMP::GetSize () {
	return (2 * sizeof (int));
}

long G3::Q3BSP_HEADER::GetSize () {
	return (4 * sizeof (char) + sizeof (int) + 17 * Lumps [0].GetSize ());
}

long G3::Q3BSP_LUMP_ENTITIES::GetSize () {
	return (Size * sizeof (char));
}

long G3::Q3BSP_LUMP_TEXTURE::GetSize () {
	return (64 * sizeof (char) + 2 * sizeof (int));
}

long G3::Q3BSP_LUMP_PLANE::GetSize () {
	return (4 * sizeof (float));
}

long G3::Q3BSP_LUMP_NODE::GetSize () {
	return (9 * sizeof (int));
}

long G3::Q3BSP_LUMP_LEAF::GetSize () {
	return (12 * sizeof (int));
}

long G3::Q3BSP_LUMP_LEAF_FACE::GetSize () {
	return (sizeof (long));
}

long G3::Q3BSP_LUMP_LEAF_BRUSH::GetSize () {
	return (sizeof (int));
}

long G3::Q3BSP_LUMP_MODEL::GetSize () {
	return (6 * sizeof (float) + 4 * sizeof (int));
}

long G3::Q3BSP_LUMP_BRUSH::GetSize () {
	return (3 * sizeof (int));
}

long G3::Q3BSP_LUMP_BRUSH_SIDE::GetSize () {
	return (2 * sizeof (int));
}

long G3::Q3BSP_LUMP_VERTEX::GetSize () {
	return (10 * sizeof (float) + sizeof (uint));
}

long G3::Q3BSP_LUMP_MESH_VERTEX::GetSize () {
	return (sizeof (int));
}

long G3::Q3BSP_LUMP_EFFECT::GetSize () {
	return (64 * sizeof (char) + 2 * sizeof (int));
}

long G3::Q3BSP_LUMP_FACE::GetSize () {
	return (14 * sizeof (int) + 12 * sizeof (float));
}

long G3::Q3BSP_LUMP_LIGHTMAP::GetSize () {
	return (49152 * sizeof (byte));
}

long G3::Q3BSP_LUMP_LIGHTVOL::GetSize () {
	return (8 * sizeof (byte));
}

long G3::Q3BSP_LUMP_VISIBILITY::GetSize () {
	return (2 * sizeof (int) + NumClusters * BytesPerCluster * sizeof (byte));
}
//}

//==============================================================================
// Bounding Box conversions
//==============================================================================
G3::AABB3F G3::Q3BSP_LUMP_LEAF::GetBBox () {
    G3::AABB3F box;

    box.Minimum.x = BBox [0]; box.Maximum.x = BBox [3];
    box.Minimum.y = BBox [1]; box.Maximum.y = BBox [4];
    box.Minimum.z = BBox [2]; box.Maximum.z = BBox [5];

    return box;
}

//==============================================================================
// Functions that read the lumps
//==============================================================================
//{
bool G3::Q3BSP_LUMP::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) &FileOffset, sizeof (int));
	bytesread += Stream->Read ((byte *) &FileLength, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP::Read: Failed to read a lump descriptor (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_HEADER::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_HEADER::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) Signature, 4 * sizeof (char));
	bytesread += Stream->Read ((byte *) &Version, sizeof (int));

	if (bytesread != 4 * sizeof (char) + sizeof (int)) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_HEADER::Read: Failed to read Q3 IBSP Signature and Version (%d bytes)..\n", 4 * sizeof (char) + sizeof (int));
		return false;
	}

	for (int i=0; i<17; i++) {
		if (!Lumps [i].Read (Stream, Log))
			return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_ENTITIES::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_ENTITIES::Read: Stream argument is a NULL pointer");

	if (Size <= 0)
		return true;

    delete [] pEntities;

	pEntities = new char [Size];

	long bytesread = Stream->Read ((byte *) pEntities, Size * sizeof (char));

	if (bytesread != (long) (Size * sizeof (char))) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_ENTITIES::Read: Failed to read the Entities lump (%d bytes)..\n", Size * sizeof (char));
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_TEXTURE::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_TEXTURE::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) Name, 64 * sizeof (char));
	bytesread += Stream->Read ((byte *) &SurfaceFlags, sizeof (int));
	bytesread += Stream->Read ((byte *) &ContentFlags, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_TEXTURE::Read: Failed to read a Texture Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	// There's no shader?
	if (Name [0] == '\0' || strcmp (Name, "noshader") == 0)
		return true;

	// Load it in..
	// TODO:: All texture formats / shaders that contain the actual texture filenames
	string TexPath = string (Name) + ".jpg";
	Handle = TextureManager.GetHandle (TexPath.c_str ());

	return true;
}

bool G3::Q3BSP_LUMP_PLANE::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_PLANE::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) Normal, 3 * sizeof (float));
	bytesread += Stream->Read ((byte *) &Dist, sizeof (float));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_PLANE::Read: Failed to read a Plane Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_NODE::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_NODE::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	int BBoxTemp [6];

	bytesread += Stream->Read ((byte *) &Plane, sizeof (int));
	bytesread += Stream->Read ((byte *) Children, 2 * sizeof (int));
	bytesread += Stream->Read ((byte *) BBoxTemp, 6 * sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_NODE::Read: Failed to read a Node Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	for (int i=0; i<6; i++)
		BBox [i] = (float) BBoxTemp [i];

	return true;
}

bool G3::Q3BSP_LUMP_LEAF::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_LEAF::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	int BBoxTemp [6];

	bytesread += Stream->Read ((byte *) &Cluster, sizeof (int));
	bytesread += Stream->Read ((byte *) &Area, sizeof (int));
	bytesread += Stream->Read ((byte *) BBoxTemp, 6 * sizeof (int));
	bytesread += Stream->Read ((byte *) &FirstFace, sizeof (int));
	bytesread += Stream->Read ((byte *) &NumFaces, sizeof (int));
	bytesread += Stream->Read ((byte *) &FirstBrush, sizeof (int));
	bytesread += Stream->Read ((byte *) &NumBrushes, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_LEAF::Read: Failed to read a Leaf Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	for (int i=0; i<6; i++)
		BBox [i] = (float) BBoxTemp [i];

	return true;
}

bool G3::Q3BSP_LUMP_LEAF_FACE::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_LEAF_FACE::Read: Stream argument is a NULL pointer");

	long bytesread = Stream->Read ((byte *) &FaceIndex, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_LEAF_FACE::Read: Failed to read a Leaf Face Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_LEAF_BRUSH::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_LEAF_BRUSH::Read: Stream argument is a NULL pointer");

	long bytesread = Stream->Read ((byte *) &BrushIndex, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_LEAF_BRUSH::Read: Failed to read a Leaf Brush Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_MODEL::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_MODEL::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) BBox, 6 * sizeof (float));
	bytesread += Stream->Read ((byte *) &FirstFace, sizeof (int));
	bytesread += Stream->Read ((byte *) &NumFaces, sizeof (int));
	bytesread += Stream->Read ((byte *) &FirstBrush, sizeof (int));
	bytesread += Stream->Read ((byte *) &NumBrushes, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_MODEL::Read: Failed to read a Model Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_BRUSH::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_BRUSH::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) &FirstSide, sizeof (int));
	bytesread += Stream->Read ((byte *) &NumSides, sizeof (int));
	bytesread += Stream->Read ((byte *) &TexIndex, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_BRUSH::Read: Failed to read a Brush Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_BRUSH_SIDE::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_BRUSH_SIDE::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) &PlaneNum, sizeof (int));
	bytesread += Stream->Read ((byte *) &TexIndex, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_BRUSH_SIDE::Read: Failed to read a Brush Side Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_VERTEX::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_VERTEX::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) Point, 3 * sizeof (float));
	bytesread += Stream->Read ((byte *) TexCoord, 2 * sizeof (float));
	bytesread += Stream->Read ((byte *) LightTexCoord, 2 * sizeof (float));
	bytesread += Stream->Read ((byte *) Normal, 3 * sizeof (float));
	bytesread += Stream->Read ((byte *) &Color, sizeof (uint));

	if (bytesread != 10 * sizeof (float) + sizeof (uint)) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_VERTEX::Read: Failed to read a vertex (%d bytes)..\n", 10 * sizeof (float) + sizeof (uint));
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_MESH_VERTEX::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_MESH_VERTEX::Read: Stream argument is a NULL pointer");

	long bytesread = Stream->Read ((byte *) &Offset, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_MESH_VERTEX::Read: Failed to read a Mesh Vertex Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_EFFECT::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_EFFECT::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) Name, 64 * sizeof (char));
	bytesread += Stream->Read ((byte *) &Brush, sizeof (int));
	bytesread += Stream->Read ((byte *) &Unknown, sizeof (int));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_EFFECT::Read: Failed to read an Effect Lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_FACE::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_FACE::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) &ShaderIndex, sizeof (int));
	bytesread += Stream->Read ((byte *) &EffectIndex, sizeof (int));
	bytesread += Stream->Read ((byte *) &FaceType, sizeof (int));
	bytesread += Stream->Read ((byte *) &FirstVert, sizeof (int));
	bytesread += Stream->Read ((byte *) &NumVerts, sizeof (int));
	bytesread += Stream->Read ((byte *) &FirstMeshVerts, sizeof (int));
	bytesread += Stream->Read ((byte *) &NumMeshVerts, sizeof (int));
	bytesread += Stream->Read ((byte *) &LightmapIndex, sizeof (int));
	bytesread += Stream->Read ((byte *) LightmapStart, 2 * sizeof (int));
	bytesread += Stream->Read ((byte *) LightmapSize, 2 * sizeof (int));
	bytesread += Stream->Read ((byte *) LightmapOrigin, 3 * sizeof (float));
	bytesread += Stream->Read ((byte *) LightmapVects, 6 * sizeof (float));
	bytesread += Stream->Read ((byte *) Normal, 3 * sizeof (float));
	bytesread += Stream->Read ((byte *) PatchSize, 2 * sizeof (int));

	if (bytesread != 14 * sizeof (int) + 12 * sizeof (float)) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_FACE::Read: Failed to read face data (%d bytes)..\n", 14 * sizeof (int) + 12 * sizeof (float));
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_LIGHTMAP::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_LIGHTMAP::Read: Stream argument is a NULL pointer");

	long bytesread = Stream->Read ((byte *) Map, 49152 * sizeof (byte));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_LIGHTMAP::Read: Failed to read Lightmap lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_LIGHTVOL::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_LIGHTVOL::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) Ambient, 3 * sizeof (byte));
	bytesread += Stream->Read ((byte *) Directional, 3 * sizeof (byte));
	bytesread += Stream->Read ((byte *) Direction, 2 * sizeof (byte));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_LIGHTVOL::Read: Failed to read Lightvolume lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}

bool G3::Q3BSP_LUMP_VISIBILITY::Read (G3::STREAM *Stream, G3::LOG *Log) {
	Assert (Stream, "Q3BSP_LUMP_VISIBILITY::Read: Stream argument is a NULL pointer");

	long bytesread = 0;

	bytesread += Stream->Read ((byte *) &NumClusters, sizeof (int));
	bytesread += Stream->Read ((byte *) &BytesPerCluster, sizeof (int));

	if (NumClusters <= 0 || BytesPerCluster <= 0) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_VISIBILITY::Read: Invalid NumClusters (%d) or BytesPerCluster (%d)..\n", NumClusters, BytesPerCluster);
		return false;
	}

    delete [] Buffer;

	Buffer = new byte [NumClusters * BytesPerCluster * sizeof (byte)];

	bytesread += Stream->Read ((byte *) Buffer, NumClusters * BytesPerCluster * sizeof (byte));

	if (bytesread != GetSize ()) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_LUMP_VISIBILITY::Read: Failed to read Visibility lump (%d bytes)..\n", GetSize ());
		return false;
	}

	return true;
}


//}

//==============================================================================
// Functions that dump the lumps
//==============================================================================
//{
void G3::Q3BSP_HEADER::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Q3 IBSP Header:\n");
		Log->Report (" Signature: %c%c%c%c\n", Signature [0], Signature [1], Signature [2], Signature [3]);
		Log->Report (" Version: 0x%X\n", Version);

		for (int i=0; i<17; i++) {
			Log->Report (" Lump %d: %s Offset: 0x%X Length: 0x%X\n", i,
						 G3::Q3BSP_LUMPNAMES [i].c_str (), Lumps [i].FileOffset, Lumps [i].FileLength);
		}
	}
}

void G3::Q3BSP_LUMP_ENTITIES::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Entities Lump:\n");
		Log->Report (" Size: %d\n", Size);
		Log->Report (" Entities: \"");

		if (!pEntities || Size <= 0) {
			Log->Report ("(null)\n");
			return;
		}

		// Since The G3::LOG only has a 1024 bytes of buffer
		// we split the entities lump into smaller sections
		string e;
		int i = 0;
		long j = 0;
		while (j < Size) {
			e = "";
			for (i=0; i<512; i++) {
				if (j + i < Size)
					e += pEntities [j + i];
				else break;
			}
			Log->Report ("%s", e.c_str ());
			j += i;
		}

		Log->Report ("\"\n");
	}
}

void G3::Q3BSP_LUMP_TEXTURE::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Texture:\n");

		string name;

		for (int i=0; i<64; i++)
			name += Name [i];

		Log->Report (" Name: \"%s\"\n", name.c_str ());
		Log->Report (" SurfaceFlags: 0x%X\n", SurfaceFlags);
		Log->Report (" ContentFlags: 0x%X\n", ContentFlags);
	}
}

void G3::Q3BSP_LUMP_PLANE::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Plane:\n");
		Log->Report (" Normal: %lf, %lf, %lf\n", Normal [0], Normal [1], Normal [2]);
		Log->Report (" Dist: %lf\n", Dist);
	}
}

void G3::Q3BSP_LUMP_NODE::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Node:\n");
		Log->Report (" Plane: %d\n", Plane);
		Log->Report (" Children: %d, %d\n", Children [0], Children [1]);
		Log->Report (" BBox: (%d, %d, %d) (%d, %d, %d)\n",
					 BBox [0], BBox [1], BBox [2], BBox [3], BBox [4], BBox [5]);
	}
}

void G3::Q3BSP_LUMP_LEAF::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Leaf:\n");
		Log->Report (" Cluster: %d\n", Cluster);
		Log->Report (" Area: %d\n", Area);
		Log->Report (" BBox: (%d, %d, %d) (%d, %d, %d)\n",
					 BBox [0], BBox [1], BBox [2], BBox [3], BBox [4], BBox [5]);
		Log->Report (" FirstFace: %d\n", FirstFace);
		Log->Report (" NumFaces: %d\n", NumFaces);
		Log->Report (" FirstBrush: %d\n", FirstBrush);
		Log->Report (" NumBrushes: %d\n", NumBrushes);
	}
}

void G3::Q3BSP_LUMP_LEAF_FACE::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Leaf Face:\n");
		Log->Report (" FaceIndex: %d\n", FaceIndex);
	}
}

void G3::Q3BSP_LUMP_LEAF_BRUSH::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Leaf Brush:\n");
		Log->Report (" BrushIndex: %d\n", BrushIndex);
	}
}

void G3::Q3BSP_LUMP_MODEL::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Model:\n");
		Log->Report (" BBox: (%f, %f, %f) (%f, %f, %f)\n",
					 BBox [0], BBox [1], BBox [2], BBox [3], BBox [4], BBox [5]);
		Log->Report (" FirstFace: %d\n", FirstFace);
		Log->Report (" NumFaces: %d\n", NumFaces);
		Log->Report (" FirstBrush: %d\n", FirstBrush);
		Log->Report (" NumBrushes: %d\n", NumBrushes);
	}
}

void G3::Q3BSP_LUMP_BRUSH::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Brush:\n");
		Log->Report (" FirstSide: %d\n", FirstSide);
		Log->Report (" NumSides: %d\n", NumSides);
		Log->Report (" TexIndex: %d\n", TexIndex);
	}
}

void G3::Q3BSP_LUMP_BRUSH_SIDE::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Brush Side:\n");
		Log->Report (" PlaneNum: %d\n", PlaneNum);
		Log->Report (" TexIndex: %d\n", TexIndex);
	}
}

void G3::Q3BSP_LUMP_VERTEX::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Vertex:\n");
		Log->Report (" Point: %f, %f, %f\n", Point [0], Point [1], Point [2]);
		Log->Report (" TexCoord: %f, %f\n", TexCoord [0], TexCoord [1]);
		Log->Report (" LightmapTexCoord: %f, %f\n", LightTexCoord [0], LightTexCoord [1]);
		Log->Report (" Normal: %f, %f, %f\n", Normal [0], Normal [1], Normal [2]);
		Log->Report (" Color: 0x%X\n", Color);
	}
}

void G3::Q3BSP_LUMP_MESH_VERTEX::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Mesh Vertex:\n");
		Log->Report (" Offset: 0x%X\n", Offset);
	}
}

void G3::Q3BSP_LUMP_EFFECT::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Effect:\n");

		string name;

		for (int i=0; i<64; i++)
			name += Name [i];

		Log->Report (" Name: \"%s\"\n", name.c_str ());
		Log->Report (" Brush: %d\n", Brush);
		Log->Report (" Unknown: %d\n", Unknown);
	}
}

void G3::Q3BSP_LUMP_FACE::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Face:\n");
		Log->Report (" ShaderIndex: %d\n", ShaderIndex);
		Log->Report (" EffectIndex: %d\n", EffectIndex);
		Log->Report (" FaceType: %d\n", FaceType);
		Log->Report (" FirstVert: %d\n", FirstVert);
		Log->Report (" NumVerts: %d\n", NumVerts);
		Log->Report (" FirstMeshVerts: %d\n", FirstMeshVerts);
		Log->Report (" NumMeshVerts: %d\n", NumMeshVerts);
		Log->Report (" LightmapIndex: %d\n", LightmapIndex);
		Log->Report (" LightmapStart: %d, %d\n", LightmapStart [0], LightmapStart [1]);
		Log->Report (" LightmapSize: %d, %d\n", LightmapSize [0], LightmapSize [1]);
		Log->Report (" LightmapOrigin: %f, %f, %f\n", LightmapOrigin [0], LightmapOrigin [1], LightmapOrigin [2]);
		Log->Report (" LightmapVects: (%f, %f, %f) (%f, %f, %f)\n",
					 LightmapVects [0][0], LightmapVects [0][1], LightmapVects [0][2],
					 LightmapVects [1][0], LightmapVects [1][1], LightmapVects [1][2]);
		Log->Report (" Normal: %f, %f, %f\n", Normal [0], Normal [1], Normal [2]);
		Log->Report (" PatchSize: %d, %d\n", PatchSize [0], PatchSize [1]);
	}
}

void G3::Q3BSP_LUMP_LIGHTVOL::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping LightVolume:\n");
		Log->Report (" Ambient: %d, %d, %d\n", Ambient [0], Ambient [1], Ambient [2]);
		Log->Report (" Directional: %d, %d, %d\n", Directional [0], Directional [1], Directional [2]);
		Log->Report (" Direction: %d, %d\n", Direction [0], Direction [1]);
	}
}

void G3::Q3BSP_LUMP_VISIBILITY::Dump (G3::LOG *Log) {
	if (Log) {
		Log->Report ("\nDumping Visibility:\n");
		Log->Report (" NumClusters: %d\n", NumClusters);
		Log->Report (" BytesPerCluster: %d\n", BytesPerCluster);
	}
}


//}

//==============================================================================
// Lump-specific functions
//==============================================================================
G3::PLANE3F G3::Q3BSP_LUMP_PLANE::GetPlane () {
	return G3::PLANE3F (Normal [0], Normal [1], Normal [2], -Dist);
}

void G3::Q3BSP_LUMP_PLANE::SetPlane (G3::PLANE3F &p) {
	Normal [0] = p.n.x;
	Normal [1] = p.n.y;
	Normal [2] = p.n.z;
	Dist = p.d;
}

VEC3F G3::Q3BSP_LUMP_PLANE::GetNormal () {
	return VEC3F (Normal [0], Normal [1], Normal [2]);
}

G3::COL4D G3::Q3BSP_LUMP_VERTEX::GetColor () {
	G3::COL4D c;

#if SDL_BYTEORDER == SDL_LIL_ENDIAN
	c.r = (double) (Color & 0xFF);
	c.g = (double) ((Color & 0xFF00) >> 8);
	c.b = (double) ((Color & 0xFF0000) >> 16);
	c.a = (double) ((Color & 0xFF000000) >> 24);
#else
	c.a = (double) (Color & 0xFF);
	c.b = (double) ((Color & 0xFF00) >> 8);
	c.g = (double) ((Color & 0xFF0000) >> 16);
	c.r = (double) ((Color & 0xFF000000) >> 24);
#endif

	c.r /= 255.0;
	c.g /= 255.0;
	c.b /= 255.0;
	c.a /= 255.0;

	return c;
}

//==============================================================================
// Functions that clear the lumps
//==============================================================================
//{
void G3::Q3BSP_LUMP::Clear () {
	FileOffset = 0;
	FileLength = 0;
}

void G3::Q3BSP_HEADER::Clear () {
	memset (Signature, 0, 4 * sizeof (char));

	for (int i=0; i<17; i++)
		Lumps [i].Clear ();

	Version = 0;
}

void G3::Q3BSP_LUMP_ENTITIES::Clear () {
    delete [] pEntities;
	pEntities = NULL;
	Size = 0;
}

void G3::Q3BSP_LUMP_VISIBILITY::Clear () {
    delete [] Buffer;
	Buffer = NULL;
	NumClusters = 0;
	BytesPerCluster = 0;
}
//}

//==============================================================================
// Dumps the whole BSP file
//==============================================================================
void G3::Q3BSP_NODE::Dump () {
	int i = 0;

	Header.Dump (Log);
	Entities.Dump (Log);

	for (i=0; i<(int)Textures.size (); i++)
		Textures [i].Dump (Log);
	for (i=0; i<(int)Planes.size (); i++)
		Planes [i].Dump (Log);
	for (i=0; i<(int)Nodes.size (); i++)
		Nodes [i].Dump (Log);
	for (i=0; i<(int)Leaves.size (); i++)
		Leaves [i].Dump (Log);
	for (i=0; i<(int)LeafFaces.size (); i++)
		LeafFaces [i].Dump (Log);
	for (i=0; i<(int)LeafBrushes.size (); i++)
		LeafBrushes [i].Dump (Log);
	for (i=0; i<(int)Models.size (); i++)
		Models [i].Dump (Log);
	for (i=0; i<(int)Brushes.size (); i++)
		Brushes [i].Dump (Log);
	for (i=0; i<(int)BrushSides.size (); i++)
		BrushSides [i].Dump (Log);
	for (i=0; i<(int)Vertices.size (); i++)
		Vertices [i].Dump (Log);
	for (i=0; i<(int)MeshVertices.size (); i++)
		MeshVertices [i].Dump (Log);
	for (i=0; i<(int)Effects.size (); i++)
		Effects [i].Dump (Log);
	for (i=0; i<(int)Faces.size (); i++)
		Faces [i].Dump (Log);
	for (i=0; i<(int)LightVols.size (); i++)
		LightVols [i].Dump (Log);

	VisData.Dump (Log);
}

//==============================================================================
// Functions for loading Q3 BSP lumps
//==============================================================================
//{
bool G3::Q3BSP_NODE::ReadHeader (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadHeader: Stream argument is a NULL pointer");

	if (!Header.Read (Stream, Log)) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_NODE::ReadHeader: Failed to read Q3 IBSP header (%d bytes)..\n", Header.GetSize ());
		return false;
	}

	if (strncmp (Header.Signature, "IBSP", 4) != 0) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_NODE::ReadHeader: Not a valid Q3 IBSP file..\n");
		return false;
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadTextures (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadTextures: Stream argument is a NULL pointer");

	Q3BSP_LUMP_TEXTURE lt;
	int NumTextures = Header.Lumps [Q3BSPL_Textures].FileLength / lt.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Textures].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumTextures; i++) {
		if (lt.Read (Stream, Log))
			Textures.push_back (lt);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadEntities (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadEntities: Stream argument is a NULL pointer");

	Entities.Size = Header.Lumps [Q3BSPL_Entities].FileLength;

	Stream->Seek (Header.Lumps [Q3BSPL_Entities].FileOffset, G3::STREAM::STREAM_SET);

	if (!Entities.Read (Stream, Log))
		return false;

	return true;
}

bool G3::Q3BSP_NODE::ReadPlanes (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadPlanes: Stream argument is a NULL pointer");

	Q3BSP_LUMP_PLANE lp;
	int NumPlanes = Header.Lumps [Q3BSPL_Planes].FileLength / lp.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Planes].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumPlanes; i++) {
		if (lp.Read (Stream, Log))
			Planes.push_back (lp);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadNodes (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadNodes: Stream argument is a NULL pointer");

	Q3BSP_LUMP_NODE ln;
	int NumNodes = Header.Lumps [Q3BSPL_Nodes].FileLength / ln.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Nodes].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumNodes; i++) {
		if (ln.Read (Stream, Log))
			Nodes.push_back (ln);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadLeaves (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadLeaves: Stream argument is a NULL pointer");

	Q3BSP_LUMP_LEAF ll;
	int NumLeaves = Header.Lumps [Q3BSPL_Leaves].FileLength / ll.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Leaves].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumLeaves; i++) {
		if (ll.Read (Stream, Log))
			Leaves.push_back (ll);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadLeafFaces (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadLeafFaces: Stream argument is a NULL pointer");

	Q3BSP_LUMP_LEAF_FACE llf;
	int NumLeafFaces = Header.Lumps [Q3BSPL_LeafFaces].FileLength / llf.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_LeafFaces].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumLeafFaces; i++) {
		if (llf.Read (Stream, Log))
			LeafFaces.push_back (llf);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadLeafBrushes (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadLeafBrushes: Stream argument is a NULL pointer");

	Q3BSP_LUMP_LEAF_BRUSH llb;
	int NumLeafBrushes = Header.Lumps [Q3BSPL_LeafBrushes].FileLength / llb.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_LeafBrushes].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumLeafBrushes; i++) {
		if (llb.Read (Stream, Log))
			LeafBrushes.push_back (llb);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadModels (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadModels: Stream argument is a NULL pointer");

	Q3BSP_LUMP_MODEL lm;
	int NumModels = Header.Lumps [Q3BSPL_Models].FileLength / lm.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Models].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumModels; i++) {
		if (lm.Read (Stream, Log))
			Models.push_back (lm);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadBrushes (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadBrushes: Stream argument is a NULL pointer");

	Q3BSP_LUMP_BRUSH lb;
	int NumBrushes = Header.Lumps [Q3BSPL_Brushes].FileLength / lb.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Brushes].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumBrushes; i++) {
		if (lb.Read (Stream, Log))
			Brushes.push_back (lb);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadBrushSides (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadBrushSides: Stream argument is a NULL pointer");

	Q3BSP_LUMP_BRUSH_SIDE lbs;
	int NumBrushSides = Header.Lumps [Q3BSPL_BrushSides].FileLength / lbs.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_BrushSides].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumBrushSides; i++) {
		if (lbs.Read (Stream, Log))
			BrushSides.push_back (lbs);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadVertices (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadVertices: Stream argument is a NULL pointer");

	Q3BSP_LUMP_VERTEX lv;
	int NumVerts = Header.Lumps [Q3BSPL_Vertices].FileLength / lv.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Vertices].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumVerts; i++) {
		if (lv.Read (Stream, Log))
			Vertices.push_back (lv);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadMeshVertices (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadMeshVertices: Stream argument is a NULL pointer");

	Q3BSP_LUMP_MESH_VERTEX lmv;
	int NumMeshVerts = Header.Lumps [Q3BSPL_MeshIndices].FileLength / lmv.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_MeshIndices].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumMeshVerts; i++) {
		if (lmv.Read (Stream, Log))
			MeshVertices.push_back (lmv);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadEffects (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadEffects: Stream argument is a NULL pointer");

	Q3BSP_LUMP_EFFECT le;
	int NumEffects = Header.Lumps [Q3BSPL_Effects].FileLength / le.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Effects].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumEffects; i++) {
		if (le.Read (Stream, Log))
			Effects.push_back (le);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadFaces (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadFaces: Stream argument is a NULL pointer");

	Q3BSP_LUMP_FACE lf;
	int NumFaces = Header.Lumps [Q3BSPL_Faces].FileLength / lf.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Faces].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumFaces; i++) {
		if (lf.Read (Stream, Log))
			Faces.push_back (lf);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadLightmaps (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadLightmaps: Stream argument is a NULL pointer");

	Q3BSP_LUMP_LIGHTMAP ll;
	int NumLightmaps = Header.Lumps [Q3BSPL_Lightmaps].FileLength / ll.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_Lightmaps].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumLightmaps; i++) {
		if (ll.Read (Stream, Log))
			Lightmaps.push_back (ll);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadLightVols (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadLightVols: Stream argument is a NULL pointer");

	Q3BSP_LUMP_LIGHTVOL lv;
	int NumLightVols = Header.Lumps [Q3BSPL_LightVols].FileLength / lv.GetSize ();

	Stream->Seek (Header.Lumps [Q3BSPL_LightVols].FileOffset, G3::STREAM::STREAM_SET);

	for (int i=0; i<NumLightVols; i++) {
		if (lv.Read (Stream, Log))
			LightVols.push_back (lv);
	}

	return true;
}

bool G3::Q3BSP_NODE::ReadVisData (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::ReadVisData: Stream argument is a NULL pointer");

	Stream->Seek (Header.Lumps [Q3BSPL_VisData].FileOffset, G3::STREAM::STREAM_SET);

	if (!VisData.Read (Stream, Log))
		return false;

	return true;
}


//}

//==============================================================================
// Loads all Q3 BSP lumps
//==============================================================================
bool G3::Q3BSP_NODE::Load (G3::STREAM *Stream) {
	Assert (Stream, "Q3BSP_NODE::Load: Stream argument is a NULL pointer");

	if (!ReadHeader (Stream))
		return false;
	if (!ReadEntities (Stream))
		return false;
	if (!ReadTextures (Stream))
		return false;
	if (!ReadPlanes (Stream))
		return false;
	if (!ReadNodes (Stream))
		return false;
	if (!ReadLeaves (Stream))
		return false;
	if (!ReadLeafFaces (Stream))
		return false;
	if (!ReadLeafBrushes (Stream))
		return false;
	if (!ReadModels (Stream))
		return false;
	if (!ReadBrushes (Stream))
		return false;
	if (!ReadBrushSides (Stream))
		return false;
	if (!ReadVertices (Stream))
		return false;
	if (!ReadMeshVertices (Stream))
		return false;
	if (!ReadEffects (Stream))
		return false;
	if (!ReadFaces (Stream))
		return false;
	if (!ReadLightmaps (Stream))
		return false;
	if (!ReadLightVols (Stream))
		return false;
	if (!ReadVisData (Stream))
		return false;

	// Mark the start of the next file in the stream
	Stream->Flush ();
	//Stream->SetFileStart ();

	return true;
}

//==============================================================================
// Finds the leaf the Position lies in
//==============================================================================
int G3::Q3BSP_NODE::FindLeaf (VEC3F Position) {
	int nID = 0;
	float dist = 0.0;

	while (nID >= 0) {
		if (nID > (int) Nodes.size ())
			break;

		if (Nodes [nID].Plane > (int) Planes.size ())
			break;

		// Find the distance of the Point from the node splitting plane
		dist = Planes [Nodes [nID].Plane].GetNormal () * Position - Planes [Nodes [nID].Plane].Dist;

		if (dist >= 0.0f)
			nID = Nodes [nID].Children [0]; // Front node
		else
			nID = Nodes [nID].Children [1]; // Back node
	}
	// Leaf indexes are negative in value
	return (-nID - 1);
}

//==============================================================================
// Tests if a cluster is visible from another
//==============================================================================
bool G3::Q3BSP_NODE::IsClusterVisible (int visCluster, int testCluster) {
	if (!VisData.Buffer) {
		if (Log)
			Log->Report ("ERROR! Q3BSP_NODE::IsClusterVisible: VisData not loaded..\n");
		return false;
	}

	// A cluster that is outside of the map?
	if (visCluster < 0)
		return false;

	// (testCluster >> 3) gets the byte the testCluster information is contained in
	int cID = visCluster * VisData.BytesPerCluster + (testCluster >> 3);

	// The rest of testCluster's bits specify the bit that we are interested in
	return ((VisData.Buffer [cID]) & (1 << (testCluster & 7)));
}

//==============================================================================
// Finds all faces that are visible from the Position
//==============================================================================
int G3::Q3BSP_NODE::FindVisibleFaces (FRUSTUM &ViewFrustum) {
	int i = 0, j = 0, f = 0;
	FRUSTUM_CLASSIFIC classific;
	AABB3F box;

	LastLeaf = FindLeaf (ViewFrustum.Origin);

	VisibleFaces.clear ();

	LeavesInFrustum.clear ();
	LeavesSpanFrustum.clear ();

	for (i=0; i<(int)Leaves.size (); i++) {
		if (i == LastLeaf || IsClusterVisible (Leaves [LastLeaf].Cluster, Leaves [i].Cluster)) {
			if (UseFrustumCulling) {
			    box = Leaves [i].GetBBox ();

                //! \bug Doesn't work correctly anymore
                classific = ViewFrustum.IsBoxIn (box);
				if (classific != GFC_OUT) {
				    if (RenderBBoxes) {
                        if (classific == GFC_IN)
                            LeavesInFrustum.insert (i);
                        else if (classific == GFC_HALF)
                            LeavesSpanFrustum.insert (i);
				    }

					for (j=0; j<Leaves [i].NumFaces; j++) {
						f = j + Leaves [i].FirstFace;
						VisibleFaces.insert (LeafFaces [f].FaceIndex);
					}
				}
			} else {
				for (j=0; j<Leaves [i].NumFaces; j++) {
					f = j + Leaves [i].FirstFace;
					VisibleFaces.insert (LeafFaces [f].FaceIndex);
				}
			}
		}
	}
	return (int) VisibleFaces.size ();
}

//==============================================================================
// Traces a ray in a node
//==============================================================================
void G3::Q3BSP_NODE::CheckNode (int nID, float StartF, float EndF, VEC3F &Start, VEC3F &End) {
	int i = 0;

	// Have we reached a leaf?
	if (nID < 0) {
		// Invalid BSP data?
		if ((-nID - 1) < 0 || (-nID - 1) > (int) Leaves.size ()) {
			if (Log)
				Log->Report ("ERROR! Q3BSP_NODE::CheckNode: Invalid data (Leaf: %d/%d)\n", -nID - 1, Leaves.size ());
			return;
		}

		Q3BSP_LUMP_LEAF *Leaf = &Leaves [-nID - 1];
		Q3BSP_LUMP_BRUSH *Brush = NULL;
		for (i=0; i<Leaf->NumBrushes; i++) {
			// Invalid BSP data?
			if (Leaf->FirstBrush + i < 0 || Leaf->FirstBrush + i > (int) LeafBrushes.size () ||
				LeafBrushes [Leaf->FirstBrush + i].BrushIndex < 0 || LeafBrushes [Leaf->FirstBrush + i].BrushIndex > (int) Brushes.size ()) {
				if (Leaf->FirstBrush + i < 0 || Leaf->FirstBrush + i > (int) LeafBrushes.size ()) {
					if (Log)
						Log->Report ("ERROR! Q3BSP_NODE::CheckNode: Invalid data (Leaf: %d LeafBrush: %d/%d)\n", -nID - 1, Leaf->FirstBrush + i, LeafBrushes.size ());
					return;
				}
				if (LeafBrushes [Leaf->FirstBrush + i].BrushIndex < 0 || LeafBrushes [Leaf->FirstBrush + i].BrushIndex > (int) Brushes.size ()) {
					if (Log)
						Log->Report ("ERROR! Q3BSP_NODE::CheckNode: Invalid data (Leaf: %d LeafBrush: %d Brush: %d/%d)\n", -nID - 1, Leaf->FirstBrush + i, LeafBrushes [Leaf->FirstBrush + i].BrushIndex, Brushes.size ());
					return;
				}
			}

			Brush = &Brushes [LeafBrushes [Leaf->FirstBrush + i].BrushIndex];

			// Is this a valid texture index?
			if (Brush->TexIndex < 0 || Brush->TexIndex > (int) Textures.size ()) {
				if (Log)
					Log->Report ("ERROR! Q3BSP_NODE::CheckNode: Invalid data (Leaf: %d Brush: %d Texture: %d/%d)\n", -nID - 1, LeafBrushes [Leaf->FirstBrush + i].BrushIndex, Brush->TexIndex, Textures.size ());
				return;
			}

			// Make sure it is not an empty brush
			// Also check if the brush is actually solid
			if (Brush->NumSides > 0 &&
				(Textures [Brush->TexIndex].ContentFlags & Q3BSPCF_Solid)) {

				CheckBrush (Brush);

				// Mark the brush
				if (G3_TESTFLAG (LastIntersection.Flags, LastIntersection.INT_LINE_FRAG))
					LastIntersectionBrush = LeafBrushes [Leaf->FirstBrush + i].BrushIndex;
			}
		}

		return;
	}

	// Invalid BSP data?
	if (nID > (int) Nodes.size () || Nodes [nID].Plane < 0 ||
		Nodes [nID].Plane > (int) Planes.size ()) {
		if (Log)
				Log->Report ("ERROR! Q3BSP_NODE::CheckNode: Invalid data (Node: %d/%d Plane: %d/%d)\n", nID, Nodes.size (), Nodes [nID].Plane, Planes.size ());
		return;
	}

	Q3BSP_LUMP_NODE *Node = &Nodes [nID];
	Q3BSP_LUMP_PLANE *Plane = &Planes [Node->Plane];
	VEC3F Normal = Plane->GetNormal ();

	// The distances of each point from the splitting plane
	float StartD = Start * Normal - Plane->Dist;
	float EndD = End * Normal - Plane->Dist;

	// Both points are in front of the plane?
	if (StartD >= 0.0f && EndD >= 0.0f)
		CheckNode (Node->Children [0], StartF, EndF, Start, End);
	// Both points are behind the plane?
	else if (StartD < 0.0f && EndD < 0.0f)
		CheckNode (Node->Children [1], StartF, EndF, Start, End);
	// The line spans the splitting plane?
	else {
		float frac1 = 0.0f, frac2 = 0.0f, MiddleF = 0.0f;
		int Side = 0;
		VEC3F Middle;

		// Split the segment into two
		if (StartD < EndD) {
			// Back
			Side = 1;
			frac1 = (StartD + G3_EPSILON) / (StartD - EndD);
			frac2 = (StartD + G3_EPSILON) / (StartD - EndD);
		} else if (EndD < StartD) {
			// Front
			Side = 0;
			frac1 = (StartD + G3_EPSILON) / (StartD - EndD);
			frac2 = (StartD - G3_EPSILON) / (StartD - EndD);
		} else {
			// Front
			Side = 0;
			frac1 = 1.0f;
			frac2 = 0.0f;
		}

		// Clip values if neccessary
		if (frac1 < 0.0f) frac1 = 0.0f;
		else if (frac1 > 1.0f) frac1 = 1.0f;
		if (frac2 < 0.0f) frac2 = 0.0f;
		else if (frac2 > 1.0f) frac2 = 1.0f;

		// Calculate the middle point for the first side
		MiddleF = StartF + (EndF - StartF) * frac1;
		Middle = Start + (End - Start) * frac1;

		// Check the first side
		CheckNode (Node->Children [Side], StartF, MiddleF, Start, Middle);

		// Calculate the middle point for the second side
		MiddleF = StartF + (EndF - StartF) * frac2;
		Middle = Start + (End - Start) * frac2;

		// Check the second side
		CheckNode (Node->Children [!Side], MiddleF, EndF, Middle, End);
	}
}

//==============================================================================
// Traces a ray in a brush
//==============================================================================
void G3::Q3BSP_NODE::CheckBrush (Q3BSP_LUMP_BRUSH *Brush) {
	Assert (Brush, "Q3BSP_NODE::CheckBrush: Brush argument is a NULL pointer");

	int i = 0;
	Q3BSP_LUMP_BRUSH_SIDE *BrushSide = NULL;
	Q3BSP_LUMP_PLANE *Plane = NULL;
	float StartF = -1.0f, EndF = 1.0f;
	float StartD = 0.0f, EndD = 0.0f;
	float frac = 0.0f;
	bool StartsOut = false;
	bool EndsOut = false;

	for (i=0; i<Brush->NumSides; i++) {
		// Is the BSP data valid?
		if (Brush->FirstSide + i < 0 || Brush->FirstSide + i > (int) BrushSides.size () ||
			BrushSides [Brush->FirstSide + i].PlaneNum < 0 ||
			BrushSides [Brush->FirstSide + i].PlaneNum > (int) Planes.size ()) {
			if (Brush->FirstSide + i < 0 || Brush->FirstSide + i > (int) BrushSides.size ()) {
				if (Log)
					Log->Report ("ERROR! Q3BSP_NODE::CheckBrush: Invalid data (BrushSide %d/%d)\n", Brush->FirstSide + i, BrushSides.size ());
				return;
			}
			if (BrushSides [Brush->FirstSide + i].PlaneNum < 0 || BrushSides [Brush->FirstSide + i].PlaneNum > (int) Planes.size ()) {
				if (Log)
					Log->Report ("ERROR! Q3BSP_NODE::CheckBrush: Invalid data (BrushSide %d Plane %d/%d)\n", Brush->FirstSide + i, BrushSides [Brush->FirstSide + i].PlaneNum, Planes.size ());
				return;
			}
		}

		BrushSide = &BrushSides [Brush->FirstSide + i];
		Plane = &Planes [BrushSide->PlaneNum];

		StartD = LastRayStart * Plane->GetNormal () - Plane->Dist;
		EndD = LastRayEnd * Plane->GetNormal () - Plane->Dist;

		if (StartD > 0.0f)
			StartsOut = true;

		if (EndD > 0.0f)
			EndsOut = true;

		// If both are in front of the plane, then its outside of this brush
		if (StartD > 0.0f && EndD > 0.0f)
			return;
		// Both are behind this plane, it will get clipped by another one
		if (StartD <= 0.0f && EndD <= 0.0f)
			continue;

		// Line is entering into the brush?
		if (StartD > EndD) {
			frac = (StartD - G3_EPSILON) / (StartD - EndD);
			if (frac > StartF) {
				StartF = frac;
				LastIntersectionStartPlane = BrushSide->PlaneNum;
			}
		// Line is leaving the brush?
		} else {
			frac = (StartD + G3_EPSILON) / (StartD - EndD);
			if (frac < EndF) {
				EndF = frac;
				LastIntersectionEndPlane = BrushSide->PlaneNum;
			}
		}
	}

	if (!StartsOut) {
		if (!EndsOut)
			G3_SETFLAG (LastIntersection.Flags, G3::INTERSECTION::INT_ALL_SOLID);
		return;
	}

	if (StartF < EndF) {
		if (StartF > -1.0f && StartF < LastIntersection.Distance) {
			if (StartF < 0.0f)
				StartF = 0.0f;

			LastIntersection.Distance = StartF;
			G3_SETFLAG (LastIntersection.Flags, LastIntersection.INT_LINE_FRAG);
		}
		return;
	}

	LastIntersectionStartPlane = -1;
	LastIntersectionEndPlane = -1;
}

//==============================================================================
// Traces a ray in the BSP
//==============================================================================
INTERSECTION3F& G3::Q3BSP_NODE::TraceRay (VEC3F &Start, VEC3F &End) {
    G3_SETFLAG (LastIntersection.Flags, G3::INTERSECTION::INT_STARTS_OUT);
	LastIntersection.Distance = 1.0f;

	LastRayStart = Start;
	LastRayEnd = End;

	// Start checking from the root node
	CheckNode (0, 0.0f, 1.0f, Start, End);

	// Nothing blocked the trace?
	if (LastIntersection.Distance == 1.0f) {
		LastIntersection.Point = End;
		G3_UNSETFLAG (LastIntersection.Flags, LastIntersection.INT_LINE_FRAG);
		LastIntersectionBrush = -1;
		LastIntersectionStartPlane = -1;
		LastIntersectionEndPlane = -1;
	// Collided with something?
	} else {
		LastIntersection.Point = Start + (End - Start) * LastIntersection.Distance;
		G3_SETFLAG (LastIntersection.Flags, LastIntersection.INT_LINE_FRAG);
	}

	return LastIntersection;
}

//==============================================================================
// Renders the BSP
//==============================================================================
void G3::Q3BSP_NODE::Render (float ElapsedTime) {
	set<int>::iterator it;
	G3::COL4D Color;
	long i = 0;

	if (UsePVS) {
		it = VisibleFaces.begin ();
		while (it != VisibleFaces.end ()) {
			RenderFace (*it);
			it++;
		}
	} else {
		for (i=0; i<(int)Faces.size (); i++)
			RenderFace (i);
	}

    if (RenderBBoxes) {
        G3::AABB3F box;

        for (i=0; i<(int)Leaves.size (); i++) {
            box = Leaves [i].GetBBox ();

            if (std::binary_search (LeavesInFrustum.begin (), LeavesInFrustum.end (), i)) {
                box.Render (G3_GREEN_3F);
            } else if (std::binary_search (LeavesSpanFrustum.begin (), LeavesSpanFrustum.end (), i)) {
                box.Render (G3_RED_3F);
            } else {
                box.Render (G3_BLUE_3F);
            }
        }
    }
}

//==============================================================================
// Renders a face
//
//	TODO:: Optimize heavily
//	TODO:: Patch rendering
//==============================================================================
void G3::Q3BSP_NODE::RenderFace (int fID, uint RenderMode, float ElapsedTime) {
	if (fID < 0 || fID > (int) Faces.size () || Vertices.empty ())
		return;

	if (Faces [fID].FaceType != Q3BSPF_Polygon &&
		Faces [fID].FaceType != Q3BSPF_Mesh)
		return;

	if (!Textures [Faces [fID].ShaderIndex].Handle.IsNull ()) {
		glEnable (GL_TEXTURE_2D);
		TextureManager.Apply (Textures [Faces [fID].ShaderIndex].Handle);
	} else {
		glDisable (GL_TEXTURE_2D);
	}

	glEnableClientState (GL_VERTEX_ARRAY);
	if (RenderColorArrays)
        glEnableClientState (GL_COLOR_ARRAY);
    if (RenderTexCoordArrays)
        glEnableClientState (GL_TEXTURE_COORD_ARRAY);

	glVertexPointer (3, GL_FLOAT, Vertices [0].GetSize (), &(Vertices [Faces [fID].FirstVert].Point));
	if (RenderColorArrays)
        glColorPointer (4, GL_UNSIGNED_BYTE, Vertices [0].GetSize (), &(Vertices [Faces [fID].FirstVert].Color));
    if (RenderTexCoordArrays)
        glTexCoordPointer (2, GL_FLOAT, Vertices [0].GetSize (), &(Vertices [Faces [fID].FirstVert].TexCoord));

	glDrawElements (RenderMode, Faces [fID].NumMeshVerts, GL_UNSIGNED_INT, &MeshVertices [Faces [fID].FirstMeshVerts]);

    if (RenderTexCoordArrays)
        glDisableClientState (GL_TEXTURE_COORD_ARRAY);
	if (RenderColorArrays)
        glDisableClientState (GL_COLOR_ARRAY);
	glDisableClientState (GL_VERTEX_ARRAY);
}

//==============================================================================
// Updates the BSP
//==============================================================================
void G3::Q3BSP_NODE::Update (float ElapsedTime) {

}

//==============================================================================
// Scales the BSP
//
//	TODO:: Scale entities aswell
//==============================================================================
void G3::Q3BSP_NODE::Scale (float Factor) {
	int i = 0, j = 0;

	// TODO:: Scale entities: their positions, vectors and so on..

	for (i=0; i<(int)Planes.size (); i++)
		Planes [i].Dist *= Factor;

	for (i=0; i<(int)Nodes.size (); i++) {
		for (j=0; j<6; j++)
			Nodes [i].BBox [j] *= Factor;
	}

	for (i=0; i<(int)Leaves.size (); i++) {
		for (j=0; j<6; j++)
			Leaves [i].BBox [j] *= Factor;
	}

	for (i=0; i<(int)Models.size (); i++) {
		for (j=0; j<6; j++)
			Models [i].BBox [j] *= Factor;
	}

	for (i=0; i<(int)Vertices.size (); i++) {
		for (j=0; j<3; j++)
			Vertices [i].Point [j] *= Factor;
	}

	for (i=0; i<(int)Faces.size (); i++) {
		for (j=0; j<3; j++)
			Faces [i].LightmapOrigin [j] *= Factor;
	}
}

//==============================================================================
// Converts the BSP axis (X -> East, Y -> South, Z -> Down)
// into (X -> East, Y -> Up, Z -> South)
//
//	TODO:: Convert entities aswell
//==============================================================================
void G3::Q3BSP_NODE::ConvertAxis () {
	int i = 0;

	// TODO:: Convert entities: their positions, vectors and so on..

	for (i=0; i<(int)Planes.size (); i++)
		ConvertVectorAxis (Planes [i].Normal);

	for (i=0; i<(int)Nodes.size (); i++) {
		ConvertVectorAxis (&Nodes [i].BBox [0]);
		ConvertVectorAxis (&Nodes [i].BBox [3]);
	}

	for (i=0; i<(int)Leaves.size (); i++) {
		ConvertVectorAxis (&Leaves [i].BBox [0]);
		ConvertVectorAxis (&Leaves [i].BBox [3]);
	}

	for (i=0; i<(int)Models.size (); i++) {
		ConvertVectorAxis (&Leaves [i].BBox [0]);
		ConvertVectorAxis (&Leaves [i].BBox [3]);
	}

	for (i=0; i<(int)Vertices.size (); i++) {
		ConvertVectorAxis (Vertices [i].Point);
		ConvertVectorAxis (Vertices [i].Normal);
	}

	for (i=0; i<(int)Faces.size (); i++) {
		ConvertVectorAxis (Faces [i].LightmapOrigin);
		ConvertVectorAxis (Faces [i].Normal);
	}
}

//==============================================================================
// Converts float vectors from orientation (X -> East, Y -> South, Z -> Down)
// into (X -> East, Y -> Up, Z -> South)
//==============================================================================
void G3::Q3BSP_NODE::ConvertVectorAxis (float *v) {
	if (v) {
		float f = v [1];
		v [1] = v [2];
		v [2] = -f;
	}
}
